# Rectang geo
# collision system external to objects
# Anything that can collide must have a few things: a radius, a position  collide(self, other) method
# radius can be a float and position can be a Vec2d.
# a fish might have two collision geometries, one for the mouth and one for the body.
# we may not want fish to collide with one another as it would be to slow because it's an N^2 algorithm.
import math
from pyglet.gl import *
from vec2d import Vec2d
from creatures import Sprites, Creature, Fish, SmallFish, Eel

class CollisionGeometry:
    """All objects participating in collision must implement this 
    abstract interface. They do not necessarily need to subclass
    this class, however
    """

    collision_position = Vec2d(0,0) # collsion center point
    collision_radius = 0 # collision radius

    def on_collide(self, system, other):
        """Collision logic"""

class CollisionSystem:
    """
    A collision detection system.

    All objects that can be collided with should be added here.
    """
    
    def __init__(self):
        """Function that initializes Collision detection"""
        self.objects = set()
        # expose some set methods as our own
        self.add = self.objects.add
        self.__contains__ = self.objects.__contains__

    def remove(self, obj):
        try:
            self.objects.remove(obj)
        except KeyError:
            import traceback
            traceback.print_exc()

    def query(self, query_obj=None):
        """Test if query_obj collides with any objects in the collision system,
        return a generator yielding the objects that collide.

        query_obj must implement the CollisionGeometry interface
        """
        self.last_query_obj = query_obj # XXX for debugging
        query_position = query_obj.collision_position
        query_radius = query_obj.collision_radius
        for obj in tuple(self.objects):
            if obj is not query_obj:
                distance = (query_position - obj.collision_position).get_length()
                if distance <= query_radius + obj.collision_radius:
                    yield obj

    def collide(self, query_obj):
        """Test if query_obj collides with any objects in the collision system,
        for each collision, call the on_collide method for both objects

        query_obj must implement the CollisionGeometry interface
        """
        for collided_obj in self.query(query_obj):
            query_obj.on_collide(self, collided_obj)
            collided_obj.on_collide(self, query_obj)

    def draw(self):
        # XXX Debug
        position = self.last_query_obj.collision_position
        self.draw_circle(position.x, position.y, self.last_query_obj.collision_radius)
        for obj in self.objects:
            position = obj.collision_position
            self.draw_circle(position.x, position.y, obj.collision_radius)

    def draw_circle(self,x1, y1, radius):
        """Used to debug collision. Function to draw circle using triangle fans.
        requires, x, y position, radius. Color hard wired
        to red.
        """
        angle_inc = 2.0*math.pi / 10.0
        glBegin(GL_TRIANGLE_FAN)
        #Red
        glColor4f(1.0,0.0,0.0,0.2)
        glVertex2f(x1, y1)
        for angle in range(11):
            glVertex2f(x1 + math.sin(angle * angle_inc) * radius, 
                y1 + math.cos(angle * angle_inc) * radius)
        glEnd()
